﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;


namespace Tools
{
    /// <summary>
    ///     Http协议工具类
    /// </summary>
    public class WebHelper
    {
        private const string DefaultUserAgent =
            "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.111 Safari/537.36";

        #region Get


        /// <summary>
        ///     创建GET方式的HTTP请求
        /// </summary>
        /// <param name="host">请求的主机头：www.baidu.com/login.aspx</param>
        /// <param name="isHttps">是否为https</param>
        /// <param name="dict">请求的参数：Key:Name,Value:1</param>
        /// <param name="timeout">请求的超时时间</param>
        /// <param name="userAgent">请求的客户端浏览器信息，可以为空</param>
        /// <param name="cookies">随同HTTP请求发送的Cookie信息，如果不需要身份验证可以为空</param>
        /// <returns></returns>
        public static HttpWebResponse Get(string host, bool isHttps, Dictionary<string, object> dict, int? timeout,
                                                            string userAgent,
                                                            CookieCollection cookies)
        {

            string url = FormatUrl(host, isHttps, dict);

            if (string.IsNullOrEmpty(url))
            {
                throw new ArgumentNullException(url);
            }
            var request = (HttpWebRequest)WebRequest.Create(url);

            request.Method = WebRequestMethods.Http.Get;

            request.UserAgent = DefaultUserAgent;
            if (!string.IsNullOrEmpty(userAgent))
            {
                request.UserAgent = userAgent;
            }
            if (timeout.HasValue)
            {
                request.Timeout = timeout.Value;
            }
            if (cookies != null)
            {
                request.CookieContainer = new CookieContainer();
                request.CookieContainer.Add(cookies);
            }
            return GetResponse(request);
        }




        /// <summary>
        ///  返回内容Json实例化为对象
        /// </summary>
        /// <typeparam name="T">Json可以转换的类型</typeparam>
        /// <param name="jsonResponse">返回的带有json字符的web流，为空上报异常</param>
        /// <returns>实例化的新对象</returns>
        public static T ConvertObject<T>(HttpWebResponse jsonResponse) where T : class
        {
            Stream responseStream = jsonResponse.GetResponseStream();

            if (responseStream == null)
                throw new Exception("ResponseStream is null");

            using (var streamReader = new StreamReader(responseStream))
            {
                return (T)JsonHelper.DeserializeObject(streamReader.ReadToEnd(), typeof(T));
            }
        }

        /// <summary>
        ///     获取返回内容
        /// </summary>
        /// <param name="response"></param>
        /// <returns></returns>
        public static String GetStr(HttpWebResponse response)
        {
            Stream responseStream = response.GetResponseStream();

            if (responseStream != null)
            {
                using (var streamReader = new StreamReader(responseStream))
                {
                    return streamReader.ReadToEnd();
                }
            }

            return null;
        }

        /// <summary>
        ///     拼接GET的URL
        /// </summary>
        /// <param name="host">域名主机头</param>
        /// <param name="isHttps">是否为https</param>
        /// <param name="parmDict">参数</param>
        /// <returns>GET类型的URL</returns>
        public static string FormatUrl(string host, bool isHttps, Dictionary<string, Object> parmDict)
        {
            string url = isHttps ? "Https://{0}?{1}" : "Http://{0}?{1}";

            const string parmStr = "{0}={1}&";
            string parmUrl = parmDict.Aggregate("",
                                                (current, item) =>
                                                current + string.Format(parmStr, item.Key, item.Value));

            return string.Format(url, host, parmUrl).Replace("{0}={1}&", "");
        }

        #endregion

        #region Async

        public class RequestState
        {
            // This class stores the State of the request.
            const int BUFFER_SIZE = 1024;
            public StringBuilder requestData;
            public byte[] BufferRead;
            public HttpWebRequest request;
            public HttpWebResponse response;
            public Stream streamResponse;
            public RequestState()
            {
                BufferRead = new byte[BUFFER_SIZE];
                requestData = new StringBuilder("");
                request = null;
                streamResponse = null;
            }
        }



        #endregion


        #region Post

        /************
         * 1.生成body内容
         * 2.完善request其他信息
         * 
         */

        /// <summary>
        ///     Post请求，默认为UTF8编码,非文件传输
        /// </summary>
        /// <param name="url">请求的URL</param>
        /// <param name="parameters">随同请求POST的参数名称及参数值字典</param>
        /// <returns></returns>
        public static HttpWebResponse Post(string url, IDictionary<string, object> parameters)
        {
            var bodyContent = FormatBodyContent(parameters);
            return Post(url, bodyContent, "application/x-www-form-urlencoded;charset=utf-8", null, null, Encoding.UTF8, null);
        }

        public static void PostAsync(string url, IDictionary<string, object> parameters, AsyncCallback asyncCallBack, RequestState state)
        {
            var bodyContent = FormatBodyContent(parameters);
            PostAsync(url, bodyContent, "application/x-www-form-urlencoded;charset=utf-8", null, null, Encoding.UTF8, null, asyncCallBack, state);
        }

        /// <summary>
        ///       Post非文件内容
        /// </summary>
        /// <param name="url"></param>
        /// <param name="bodyStr"></param>
        /// <returns></returns>
        public static HttpWebResponse Post(string url, string bodyStr)
        {
            return Post(url, bodyStr, "application/x-www-form-urlencoded", null, null, Encoding.UTF8, null);
        }

        /// <summary>
        ///      Post请求
        /// </summary>
        /// <param name="url">请求的URL</param>
        /// <param name="bodyContent">HttpBody内容</param>
        /// <param name="contentType"></param>
        /// <param name="timeout">请求的超时时间</param>
        /// <param name="userAgent">请求的客户端浏览器信息，可以为空</param>
        /// <param name="requestEncoding">发送HTTP请求时所用的编码</param>
        /// <param name="cookies">随同HTTP请求发送的Cookie信息，如果不需要身份验证可以为空</param>
        /// <returns></returns>
        public static HttpWebResponse Post(string url, string bodyContent, string contentType,
                                                             int? timeout, string userAgent, Encoding requestEncoding,
                                                             CookieCollection cookies)
        {
            HttpWebRequest request = GenerateRequest(WebRequestMethods.Http.Post, url, bodyContent, contentType, timeout,
                userAgent, requestEncoding, cookies);
            return request.GetResponse() as HttpWebResponse;
        }

        public static void PostAsync(string url, string bodyContent, string contentType,
                                                     int? timeout, string userAgent, Encoding requestEncoding,
                                                     CookieCollection cookies, AsyncCallback asyncCallBack, RequestState requestState)
        {
            HttpWebRequest request = GenerateRequest(WebRequestMethods.Http.Post, url, bodyContent, contentType, timeout,
                userAgent, requestEncoding, cookies);
            requestState.request = request;
            request.BeginGetResponse(asyncCallBack, requestState);
        }

        /// <summary>
        ///     Post，文件传输
        /// </summary>
        /// <param name="url"></param>
        /// <param name="files"></param>
        /// <param name="timeout">超时，毫秒</param>
        /// <returns></returns>
        public static HttpWebResponse Post(string url, string[] files, int timeout)
        {


            string boundary = "----" + DateTime.Now.Ticks.ToString("x");
            string contentType = $"multipart/form-data; boundary={boundary}";

            Stream bodyContent = FileBodyContent(files, boundary);
            var request = GenerateRequest(WebRequestMethods.Http.Post, url, bodyContent, contentType, timeout, null, null);

            return GetResponse(request);
        }

        /// <summary>
        ///     
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        private static HttpWebResponse GetResponse(HttpWebRequest request)
        {
            return request.GetResponse() as HttpWebResponse;
        }

        /// <summary>
        ///     格式化Body文件内容为流，RFC2045协定
        /// </summary>
        /// <param name="files">文件本地地址</param>
        /// <param name="boundary">边界自定义</param>
        /// <returns></returns>
        public static Stream FileBodyContent(string[] files, string boundary)
        {
            Stream memStream = new MemoryStream();

            byte[] boundarybytes = Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n");
            memStream.Write(boundarybytes, 0, boundarybytes.Length);

            const string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"filename=\"{1}\"\r\n Content-Type: application/octet-stream\r\n\r\n";

            for (int i = 0; i < files.Length; i++)
            {

                string header = string.Format(headerTemplate, "file" + i, files[i]);

                byte[] headerbytes = Encoding.UTF8.GetBytes(header);

                memStream.Write(headerbytes, 0, headerbytes.Length);

                using (var fileStream = new FileStream(files[i], FileMode.Open, FileAccess.Read))
                {
                    var buffer = new byte[1024];

                    int bytesRead;

                    while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
                    {
                        memStream.Write(buffer, 0, bytesRead);
                    }
                    memStream.Write(boundarybytes, 0, boundarybytes.Length);
                }

            }
            memStream.Position = 0;
            return memStream;
        }


        /// <summary>
        ///     创建请求
        /// </summary>
        /// <param name="httpMethod">PostOrGet</param>
        /// <param name="url">请求的Url</param>
        /// <param name="bodyContent">Body内容</param>
        /// <param name="contentType">内容类型</param>
        /// <param name="timeout">超时时间，毫秒</param>
        /// <param name="userAgent">可以为空</param>
        /// <param name="requestEncoding">编码</param>
        /// <param name="cookies"></param>
        /// <returns></returns>
        private static HttpWebRequest GenerateRequest(String httpMethod, string url, String bodyContent, String contentType, Int32? timeout, string userAgent, Encoding requestEncoding, CookieCollection cookies)
        {

            Stream data = new MemoryStream(requestEncoding.GetBytes(bodyContent));
            return GenerateRequest(httpMethod, url, data, contentType, timeout, userAgent, cookies);
        }

        /// <summary>
        ///     创建Request请求
        /// </summary>
        /// <param name="httpMethod">Post/Get</param>
        /// <param name="url">请求地址</param>
        /// <param name="bodyContent">Body内容</param>
        /// <param name="contentType">内容类型</param>
        /// <param name="timeout">超时时间，毫秒</param>
        /// <param name="userAgent">可以为空</param>
        /// <param name="cookies"></param>
        /// <returns></returns>
        public static HttpWebRequest GenerateRequest(string httpMethod, string url, Stream bodyContent, string contentType, int? timeout, string userAgent, CookieCollection cookies)
        {
            if (string.IsNullOrEmpty(url))
            {
                throw new ArgumentNullException(nameof(url));
            }
            var request = WebRequest.Create(url) as HttpWebRequest;

            if (request == null)
                throw new NullReferenceException("request");

            //如果是发送HTTPS请求  
            if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase))
            {
                ServicePointManager.ServerCertificateValidationCallback = CheckValidationResult;
                request.ProtocolVersion = HttpVersion.Version10;
            }


            request.Method = httpMethod;

            request.ContentType = contentType;


            request.UserAgent = String.IsNullOrEmpty(userAgent) ? DefaultUserAgent : userAgent;

            //必须明确指定内容长度，发送Request的时候没有精确的指定这个Request的ContentLength，可能导致Framework底层无法知道你什么时候需要终止这个Request
            request.ContentLength = bodyContent.Length;

            if (timeout.HasValue)
            {
                request.Timeout = timeout.Value;
            }
            if (cookies != null)
            {
                request.CookieContainer = new CookieContainer();
                request.CookieContainer.Add(cookies);
            }

            //写入body内容
            //using (Stream stream = request.GetRequestStream())
            //{
            //    bodyContent.CopyTo(stream);
            //}


            Stream reqStream = request.GetRequestStream();
            bodyContent.Position = 0;

            byte[] buffer = new byte[1024];
            int bytesRead = 0;

            while ((bytesRead = bodyContent.Read(buffer, 0, buffer.Length)) != 0)
            {
                reqStream.Write(buffer, 0, bytesRead);
            }
            bodyContent.Close();
            reqStream.Close();

            return request;
        }



        /// <summary>
        ///       创建HttpBody
        /// 把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
        /// </summary>
        /// <param name="parameters">需要格式化的参数数组</param>
        public static string FormatBodyContent(IDictionary<string, Object> parameters)
        {
            if (parameters == null || parameters.Count == 0)
                throw new ArgumentNullException("parameters");

            var buffer = new StringBuilder();
            int i = 0;
            foreach (string key in parameters.Keys)
            {
                buffer.AppendFormat(i > 0 ? "&{0}={1}" : "{0}={1}", key, parameters[key]);
                i++;
            }
            return buffer.ToString();

        }

        /// <summary>
        ///     拆分数组
        /// </summary>
        /// <param name="content">&{0}={1}</param>
        /// <returns></returns>
        public static IDictionary<string, object> SpileBodyContent(string content)
        {
            var parmArr = content.Split('&');
            return parmArr.Select(parm => parm.Split('=')).ToDictionary<string[], string, object>(item => item[0], item => item[1]);
        }


        private static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain,
                                                  SslPolicyErrors errors)
        {
            return true; //总是接受  
        }

        #endregion
    }
}